package com.briup.pai.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.briup.pai.common.constant.AuthConstant;
import com.briup.pai.common.enums.PermissionTypeEnum;
import com.briup.pai.common.enums.ResultCodeEnum;
import com.briup.pai.common.exception.BriupAssert;
import com.briup.pai.common.utils.SecurityUtil;
import com.briup.pai.convert.PermissionConvert;
import com.briup.pai.convert.RoleConvert;
import com.briup.pai.entity.dto.AssignPermissionDTO;
import com.briup.pai.entity.dto.AssignRoleDTO;
import com.briup.pai.entity.po.*;
import com.briup.pai.entity.vo.AssignPermissionVO;
import com.briup.pai.entity.vo.AssignRoleVO;
import com.briup.pai.entity.vo.MetaVO;
import com.briup.pai.entity.vo.RouterVO;
import com.briup.pai.service.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

@Service
@CacheConfig(cacheNames = AuthConstant.AUTH_CACHE_PREFIX)
public class AuthServiceImpl implements IAuthService {

    @Autowired
    private IRoleService roleService;
    @Autowired
    private IUserService userService;
    @Autowired
    private IUserRoleService userRoleService;
    @Autowired
    private IPermissionService permissionService;
    @Autowired
    private IRolePermissionService rolePermissionService;

    @Autowired
    private RoleConvert roleConvert;
    @Autowired
    private PermissionConvert permissionConvert;

    @Override
    @Cacheable(key = "T(com.briup.pai.common.constant.AuthConstant).GET_ALL_ROLES_CACHE_KEY")
    public List<AssignRoleVO> getAllRoles() {
        return roleConvert.po2AssignRoleVOList(roleService.list());
    }

    @Override
    @Cacheable(key = "T(com.briup.pai.common.constant.AuthConstant).ROLE_CACHE_PREFIX + ':'+ #userId")
    public List<Integer> getRoleIdsByUserId(Integer userId) {
        BriupAssert.requireNotNull(
                userService,
                User::getId,
                userId,
                ResultCodeEnum.DATA_NOT_EXIST
        );
        LambdaQueryWrapper<UserRole> lqw = new LambdaQueryWrapper<>();
        lqw.eq(UserRole::getUserId, userId);
        return new ArrayList<>(userRoleService.list(lqw).stream().map(UserRole::getRoleId).toList());
    }

    @Override
    @CacheEvict(allEntries = true)
    public void assignRole(AssignRoleDTO dto) {
        // 分配角色，实际上就是删除原来用户下的所有角色，重新新增角色进去
        Integer userId = dto.getUserId();
        BriupAssert.requireNotNull(
                userService,
                User::getId,
                userId,
                ResultCodeEnum.DATA_NOT_EXIST
        );
        List<Integer> roleIds = dto.getRoleIds();
        System.out.println(roleIds);
        // 所有的角色必须存在，查出数据库中所有的角色ID，检查是否包含
        List<Integer> roleIdList = roleService.list().stream().map(Role::getId).toList();
        System.out.println(roleIdList);
        BriupAssert.requireTrue(
                CollectionUtil.containsAll(roleIdList, roleIds),
                ResultCodeEnum.DATA_NOT_EXIST
        );
        // 用户不能给自己授予角色
        BriupAssert.requireNotEqual(userId, SecurityUtil.getUserId(),ResultCodeEnum.ASSIGN_ROLE_ERROR);
        // 删除原来的角色信息
        LambdaQueryWrapper<UserRole> lqw = new LambdaQueryWrapper<>();
        lqw.eq(UserRole::getUserId, userId);
        userRoleService.remove(lqw);
        // 插入新的角色信息
        List<UserRole> list = roleIds.stream().map(roleId -> {
                    UserRole userRole = new UserRole();
                    userRole.setRoleId(roleId);
                    userRole.setUserId(userId);
                    return userRole;
                }
        ).toList();
        userRoleService.saveBatch(list);
    }

    @Override
    @Cacheable(key = "T(com.briup.pai.common.constant.AuthConstant).GET_ALL_PERMISSION_CACHE_KEY")
    public List<AssignPermissionVO> getAllPermissions() {
        // 和分页查询所有条件很像，少了分页而已
        // 查询所有目录
        LambdaQueryWrapper<Permission> lqw = new LambdaQueryWrapper<>();
        lqw.eq(Permission::getType, PermissionTypeEnum.CATALOGUE.getType())
                .orderByAsc(Permission::getSort);
        List<AssignPermissionVO> assignPermissionVOS = permissionConvert.po2AssignPermissionVOList(permissionService.list(lqw));
        List<AssignPermissionVO> list = assignPermissionVOS.stream().peek(
                catalogue -> {
                    // 查询所有菜单
                    lqw.clear();
                    lqw.eq(Permission::getParentId, catalogue.getPermissionId())
                            .eq(Permission::getType, PermissionTypeEnum.MENU.getType())
                            .orderByAsc(Permission::getSort);
                    catalogue.setChildren(permissionConvert.po2AssignPermissionVOList(permissionService.list(lqw)).stream().peek(
                            menu -> {
                                // 获取所有按钮
                                lqw.clear();
                                lqw.eq(Permission::getParentId, menu.getPermissionId())
                                        .eq(Permission::getType, PermissionTypeEnum.BUTTON.getType())
                                        .orderByAsc(Permission::getSort);
                                menu.setChildren(permissionConvert.po2AssignPermissionVOList(permissionService.list(lqw)));
                            }
                    ).toList());
                }
        ).toList();
        return new ArrayList<>(list);
    }

    @Override
    @Cacheable(key = "T(com.briup.pai.common.constant.AuthConstant).PERMISSION_CACHE_PREFIX+':'+#roleId")
    public List<Integer> getPermissionIdsByRoleId(Integer roleId) {
        Role role = BriupAssert.requireNotNull(
                roleService,
                Role::getId,
                roleId,
                ResultCodeEnum.DATA_NOT_EXIST
        );
        // 查询该角色下所有的按钮权限即可，前端会在选择后自动选择其父类的权限
        LambdaQueryWrapper<RolePermission> lqw = new LambdaQueryWrapper<>();
        lqw.eq(RolePermission::getRoleId, roleId);
        return new ArrayList<>(
                rolePermissionService.list(lqw).stream().map(
                        s-> permissionService.getById(s.getPermissionId())
                ).filter(s-> s.getType().equals(PermissionTypeEnum.BUTTON.getType()))
                        .map(Permission::getId)
                        .toList());
    }

    @Override
    @CacheEvict(allEntries = true)
    public void assignPermission(AssignPermissionDTO dto) {
        // 照葫芦画瓢，和分配角色的逻辑一模一样。只是实体变了
        Integer roleId = dto.getRoleId();
        Role role = BriupAssert.requireNotNull(
                roleService,
                Role::getId,
                roleId,
                ResultCodeEnum.DATA_NOT_EXIST
        );
        List<Integer> permissionIds = dto.getPermissionIds();
        BriupAssert.requireTrue(
                CollectionUtil.containsAll(permissionService.list().stream().map(Permission::getId).toList(),permissionIds),
                ResultCodeEnum.DATA_NOT_EXIST
        );
        LambdaQueryWrapper<RolePermission> lqw = new LambdaQueryWrapper<>();
        lqw.eq(RolePermission::getRoleId, roleId);
        rolePermissionService.remove(lqw);
        List<RolePermission> list = permissionIds.stream().map(permissionId -> {
            RolePermission rolePermission = new RolePermission();
            rolePermission.setRoleId(roleId);
            rolePermission.setPermissionId(permissionId);
            return rolePermission;
        }).toList();
        rolePermissionService.saveBatch(list);
    }

    @Override
    @Cacheable(key = "T(com.briup.pai.common.constant.AuthConstant).ROUTER_CACHE_PREFIX+':'+#userId")
    public List<RouterVO> getRouter(Integer userId) {
        List<RouterVO> routerVOS = new ArrayList<>();
        // 根据用户id，查询所有的权限树，封装到RouterVO中
        // 获取当前用户的角色Id列表
        List<Integer> roleIdsByUserId = this.getRoleIdsByUserId(userId);
        roleIdsByUserId.forEach(roleId -> {
            // 根据roleId查询所有的权限id
            LambdaQueryWrapper<RolePermission> lqw = new LambdaQueryWrapper<>();
            lqw.eq(RolePermission::getRoleId, roleId);
            List<Integer> permissionIds = rolePermissionService.list(lqw).stream().map(RolePermission::getPermissionId).toList();
            // 根据可用的权限id，获得所有的目录
            List<Permission> catalogueList = permissionIds.stream()
                    .map(permissionId -> permissionService.getById(permissionId))
                    .filter(permission -> ObjectUtil.equal(permission.getType(), PermissionTypeEnum.CATALOGUE.getType()))
                    .toList();
            catalogueList.forEach(catalogue -> {
                RouterVO catalogueRouterVO = this.buildRouterVO(catalogue);
                // 根据目录查子目录（菜单）
                List<Permission> menuList = permissionIds.stream()
                        .map(permissionId -> permissionService.getById(permissionId))
                        .filter(permission -> ObjectUtil.equal(permission.getType(), PermissionTypeEnum.MENU.getType()) && ObjectUtil.equal(permission.getParentId(), catalogue.getId()))
                        .toList();
                // 构建menuVo的路由
                List<RouterVO> menuRouterVOS = menuList.stream().map(this::buildRouterVO).toList();
                catalogueRouterVO.setChildren(menuRouterVOS);
                routerVOS.add(catalogueRouterVO);
            });
        });
        return routerVOS;
    }

    @Override
    @Cacheable(key = "T(com.briup.pai.common.constant.AuthConstant).BUTTON_CACHE_PREFIX+':'+#userId")
    public List<String> getUserButtonPermissionList(Integer userId) {
        // 上一个方法获取的是层级结构 目录 -> 菜单
        // 这个方法查询所有的按钮
        List<String> buttonPermissionList = new ArrayList<>();
        List<Integer> roleIdsByUserId = this.getRoleIdsByUserId(userId);
        roleIdsByUserId.forEach(roleId -> {
            List<Integer> permissionIdsByRoleId = this.getPermissionIdsByRoleId(roleId);
            List<String> stringList = permissionIdsByRoleId.stream()
                    .map(permissionId -> permissionService.getById(permissionId).getPerm())
                    .toList();
            buttonPermissionList.addAll(stringList);
        });
        return buttonPermissionList;
    }

    /**
     * 构建路由VO，实际上就是Permission对象 映射 到RouterVO对象
     * @param permission 目录对象
     * @return RouterVO
     */
    private RouterVO buildRouterVO(Permission permission) {
        RouterVO routerVO = new RouterVO();
        routerVO.setPath(permission.getPath());
        routerVO.setComponent(permission.getComponent());
        routerVO.setHidden(permission.getHidden().equals(1));
        // 判断permission中的title和icon是否有值。有的话就构建MetaVO对象
        if (ObjectUtil.isNotEmpty(permission.getTitle()) || ObjectUtil.isNotEmpty(permission.getIcon())) {
            MetaVO metaVO = new MetaVO();
            metaVO.setTitle(permission.getTitle());
            metaVO.setIcon(permission.getIcon());
            routerVO.setMeta(metaVO);
        }
        return routerVO;
    }
}